unit uFormBrwBase;

{$mode objfpc}{$H+}
{$i orcis.inc}

interface

uses
    Classes, SysUtils, Forms, Controls, Graphics,
    ExtCtrls, Buttons, StdCtrls, ActnList,
    Messages, Clipbrd,
    LCLIntf, LCLType, LMessages,
    uTypes, uCore, uConstants,
    uFrameManager, uMessages,
    uInterfaces, uSearchEngines,
    uModuleHistory;

type

    { TFrmBrwBase }

    TFrmBrwBase = class(TFrame, IISFrame, IISMessageTarget)
        actBrw: TActionList;
        actCBTurnOff: TAction;
        actCBTurnOn: TAction;
        actFindInPage: TAction;
        actWebShowSource: TAction;
        actWebShowResource: TAction;
        actWebShowPage: TAction;
        actNavStop: TAction;
        actNavRefresh: TAction;
        actNavForward: TAction;
        actNavBack: TAction;
        actNavGo: TAction;
        Button1: TButton;
        CheckBox1: TCheckBox;
        CheckBox2: TCheckBox;
        edtAddress: TEdit;
        edtSearch: TEdit;
        ilBrwProgress: TImageList;
        imgProgress: TImage;
        Label1: TLabel;
        lblSearchTitle: TLabel;
        lblStatus: TLabel;
        lblHint: TLabel;
        ListBox1: TListBox;
        lbxLog: TListBox;
        nbkContent: TNotebook;
        nbpPage: TPage;
        nbpResources: TPage;
        nbpSource: TPage;
        Panel1: TPanel;
        pnlSearchEdit: TPanel;
        pnlSearch: TPanel;
        pnlAddressEdit: TPanel;
        pnlAddressHintTools: TPanel;
        pnlAddressHint: TPanel;
        pnlStatus: TPanel;
        pnlClient: TPanel;
        pnlHint: TPanel;
        pnlStatusBar: TPanel;
        pnlAddress: TPanel;
        pnlTools: TPanel;
        pnlAddressBar: TPanel;
        btnNavBack: TSpeedButton;
        btnNavForward: TSpeedButton;
        btnNavRefresh: TSpeedButton;
        btnNavGo: TSpeedButton;
        btnWebShowPage: TSpeedButton;
        btnWebShowResource: TSpeedButton;
        btnWebShowSource: TSpeedButton;
        btnWebBlockContent: TSpeedButton;
        btnWebFindInPage: TSpeedButton;
        SpeedButton4: TSpeedButton;
        SpeedButton5: TSpeedButton;
        SpeedButton6: TSpeedButton;
        tmrProgress: TTimer;
        procedure actCBTurnOffExecute(Sender: TObject);
        procedure actCBTurnOnExecute(Sender: TObject);
        procedure actFindInPageExecute(Sender: TObject);
        procedure actNavBackExecute(Sender: TObject);
        procedure actNavForwardExecute(Sender: TObject);
        procedure actNavGoExecute(Sender: TObject);
        procedure actNavRefreshExecute(Sender: TObject);
        procedure actNavStopExecute(Sender: TObject);
        procedure actWebShowPageExecute(Sender: TObject);
        procedure actWebShowResourceExecute(Sender: TObject);
        procedure actWebShowSourceExecute(Sender: TObject);
        procedure Button1Click(Sender: TObject);
        procedure edtAddressChange(Sender: TObject);
        procedure edtAddressEnter(Sender: TObject);
        procedure edtAddressExit(Sender: TObject);
        procedure edtAddressKeyPress(Sender: TObject; var Key: char);
        procedure lbxLogDblClick(Sender: TObject);
        procedure lbxLogKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
        procedure pnlAddressResize(Sender: TObject);
        procedure SpeedButton6Click(Sender: TObject);
        procedure tmrProgressTimer(Sender: TObject);
    private
        FInProgress: boolean;
        FMTID: string;
        FProgress: integer;
        FTitle: string;
        FOnTitleChange: TFrameTitleChange;

        FBlockContent: boolean;

        procedure SetBlockContent(AValue: boolean);
        procedure SetInProgress(AValue: boolean);
        procedure SetOnTitleChange(AValue: TFrameTitleChange);
        procedure SetBrwStatus(const AMsg: string);
        procedure SetBrwHint(const AText: string);
        procedure SetBrwURL(const aURL: string);
        procedure DoLog(const AMsg: string); overload;


        property InProgress: boolean read FInProgress write SetInProgress;
    private
        FLastErrorCode: integer;
        FLastErrorText: string;
        // IISFrame

        function GetTitle: string;
        procedure SetLastErrorCode(AValue: integer);
        procedure SetLastErrorText(AValue: string);
        procedure SetTitle(const AValue: string);
        function GetContainer: IISFrameContainer;
        procedure SetContainer(AValue: IISFrameContainer);
    protected
        FCore: IISCore;
        FContainer: IISFrameContainer;

        procedure Init; virtual;
        procedure Done; virtual; abstract;

        procedure InitImages; virtual;

        procedure DoTitleChange(const ATitle: string);

        procedure AddressHintHide;
        procedure AddressHintShow;

        function GetTextMessage(const AMsg: TMessage): string;
        procedure LMTextMessage(var AMsg: TMessage); message ISM_TMP_TEXT;

        procedure DoSetStatus(const AMsg: string);
        procedure DoSetHint(const AText: string);

        procedure LMProgressChange(var AMsg: TMessage); message ISM_TMP_BRW_STATUS;

        procedure ProgressStart;
        procedure ProgressStop;

        procedure AdjustStatusPanel;

        // IISMessageTraget
        function ProcessMessage(AMsg: IISMessage): boolean;
        function GetMTID: string;
        procedure SetMTID(AValue: string);

        function GetErrorHTML(const ACode: integer; const AText: string): string;

        property MTID: string read FMTID write SetMTID;
        property LastErrorCode: integer read FLastErrorCode write SetLastErrorCode;
        property LastErrorText: string read FLastErrorText write SetLastErrorText;
    public
        constructor Create(TheOwner: TComponent); override;
        destructor Destroy; override;
        procedure PostTextMessage(AHandle: HWND; AMsg: cardinal; ASubMsg: WParam; AText: string);

        procedure LoadURL(const AUrl: string); virtual;

        procedure Log(const AMsg: string); overload;
        procedure Log(const AMsg, AValue: string); overload;
        procedure Log(const AMsg: string; const AValue: boolean); overload;

        function LogToClipboard: boolean;

        procedure DoBack; virtual; abstract;
        procedure DoForward; virtual; abstract;
        procedure DoRefresh; virtual; abstract;
        procedure DoGo; virtual;
        procedure DoStop; virtual; abstract;

        procedure FocusAddressField;

        property OnTitleChange: TFrameTitleChange read FOnTitleChange write SetOnTitleChange;
        property BlockContent: boolean read FBlockContent write SetBlockContent;
        property Title: string read GetTitle write SetTitle;
        property Container: IISFrameContainer read GetContainer write SetContainer;
    end;

    { TBrwContainer }

    TBrwContainer = class(TFrameContainer)
    private
        FButton: TSpeedButton;
        FButtonBar: TPanel;
        procedure SetButton(AValue: TSpeedButton);
        procedure SetButtonBar(AValue: TPanel);
    protected
        procedure Init; override;
        procedure Done; override;
    public
        property ButtonBar: TPanel read FButtonBar write SetButtonBar;
        property Button: TSpeedButton read FButton write SetButton;
    end;

var
    FrmBrwBase: TFrmBrwBase;

implementation

{$R *.lfm}

{ TBrwContainer }

procedure TBrwContainer.SetButtonBar(AValue: TPanel);
begin
    if FButtonBar = AValue then
        Exit;
    FButtonBar := AValue;
end;

procedure TBrwContainer.SetButton(AValue: TSpeedButton);
begin
    if FButton = AValue then
        Exit;
    FButton := AValue;
end;

procedure TBrwContainer.Init;
begin
    inherited Init;
    OnTitleChange := @EventTitleChange;
end;

procedure TBrwContainer.Done;
begin
    inherited Done;
end;

{ TFrmBrwBase }

procedure TFrmBrwBase.actNavGoExecute(Sender: TObject);
begin
    DoGo;
end;

procedure TFrmBrwBase.actNavRefreshExecute(Sender: TObject);
begin
    DoRefresh;
end;

procedure TFrmBrwBase.actNavStopExecute(Sender: TObject);
begin
    DoStop;
end;

procedure TFrmBrwBase.actWebShowPageExecute(Sender: TObject);
begin
    nbkContent.PageIndex := 0;
end;

procedure TFrmBrwBase.actWebShowResourceExecute(Sender: TObject);
begin
    nbkContent.PageIndex := 1;
end;

procedure TFrmBrwBase.actWebShowSourceExecute(Sender: TObject);
begin
    nbkContent.PageIndex := 2;
end;

procedure TFrmBrwBase.actNavBackExecute(Sender: TObject);
begin
    DoBack;
end;

procedure TFrmBrwBase.actCBTurnOffExecute(Sender: TObject);
begin
    BlockContent := False;
end;

procedure TFrmBrwBase.actCBTurnOnExecute(Sender: TObject);
begin
    BlockContent := True;
end;

procedure TFrmBrwBase.actFindInPageExecute(Sender: TObject);
begin
    pnlSearch.Visible := not pnlSearch.Visible;
    if pnlSearch.Visible then
        edtSearch.SetFocus;
end;

procedure TFrmBrwBase.actNavForwardExecute(Sender: TObject);
begin
    DoForward;
end;

procedure TFrmBrwBase.Button1Click(Sender: TObject);
begin
    lbxLog.Items.Clear;
end;

procedure TFrmBrwBase.edtAddressChange(Sender: TObject);
var
    s, h: string;
    p: TPoint;
    sl: TStringList;
    i: integer;
begin
    sl := TStringList.Create;
    try
        // Load fastlinks
        if FileExists('./data/flinks.dat') then
            sl.LoadFromFile('./data/flinks.dat');
        //sl.Add('');

        s := edtAddress.Text;
        for i := 0 to sl.Count - 1 do
        begin
            h := sl[i];
            if (Pos(s, h) = 1) and (s <> h) then
            begin
                p := edtAddress.CaretPos;
                edtAddress.Text := h;
                edtAddress.CaretPos := p;
                edtAddress.SelStart := Length(s);
                edtAddress.SelLength := Length(h) - Length(s);
                AddressHintShow;
                Break;
            end
            else
            begin
                AddressHintHide;
            end;
        end;
    finally
        sl.Free;
    end;
end;

procedure TFrmBrwBase.edtAddressEnter(Sender: TObject);
begin
    edtAddress.SelectAll;
end;

procedure TFrmBrwBase.edtAddressExit(Sender: TObject);
begin
    AddressHintHide;
end;

procedure TFrmBrwBase.edtAddressKeyPress(Sender: TObject; var Key: char);
var
    l: integer;
begin
    case Key of
        CSK_RETURN:
        begin
            AddressHintHide;
            Key := #0;
            DoGo;
        end;
        CSK_BACKSPACE:
        begin
            // Is selection present

            if edtAddress.SelStart > 0 then
            begin
                l := edtAddress.SelLength + 1;
                edtAddress.SelStart := edtAddress.SelStart - 1;
                edtAddress.SelLength := l;
            end;
        end;
    end;
end;

procedure TFrmBrwBase.lbxLogDblClick(Sender: TObject);
begin
    LogToClipboard;
end;

procedure TFrmBrwBase.lbxLogKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
begin
    if (Shift = [ssCtrl]) and (Key = VK_C) then
        LogToClipboard;
end;

procedure TFrmBrwBase.pnlAddressResize(Sender: TObject);
begin
    if pnlAddressHint.Visible then
        AddressHintShow;
end;

procedure TFrmBrwBase.SpeedButton6Click(Sender: TObject);
begin
    AddressHintHide;
end;

procedure TFrmBrwBase.tmrProgressTimer(Sender: TObject);
begin
    if InProgress then
    begin
        ilBrwProgress.GetBitmap(FProgress, imgProgress.Picture.Bitmap);
        Inc(FProgress);
        if FProgress > 16 then
            FProgress := 0;
    end
    else
    begin
        ilBrwProgress.GetBitmap(17, imgProgress.Picture.Bitmap);
        tmrProgress.Enabled := False;
    end;
end;

procedure TFrmBrwBase.SetOnTitleChange(AValue: TFrameTitleChange);
begin
    if FOnTitleChange = AValue then
        Exit;
    FOnTitleChange := AValue;
end;

procedure TFrmBrwBase.SetBlockContent(AValue: boolean);
begin
    if FBlockContent = AValue then
        Exit;
    FBlockContent := AValue;
    if AValue then
        btnWebBlockContent.Action := actCBTurnOff
    else
        btnWebBlockContent.Action := actCBTurnOn;
end;

function TFrmBrwBase.GetTitle: string;
begin
    Result := FTitle;
end;

procedure TFrmBrwBase.SetLastErrorCode(AValue: integer);
begin
    if FLastErrorCode = AValue then
        Exit;
    FLastErrorCode := AValue;
end;

procedure TFrmBrwBase.SetLastErrorText(AValue: string);
begin
    if FLastErrorText = AValue then
        Exit;
    FLastErrorText := AValue;
end;

procedure TFrmBrwBase.SetInProgress(AValue: boolean);
begin
    if FInProgress = AValue then
        Exit;
    FInProgress := AValue;
    if FInProgress then
    begin
        FProgress := 0;
        tmrProgress.Enabled := True;
        btnNavRefresh.Action := actNavStop;
    end
    else
    begin
        btnNavRefresh.Action := actNavRefresh;
    end;
end;

procedure TFrmBrwBase.SetMTID(AValue: string);
begin
    if FMTID = AValue then
        Exit;
    FMTID := AValue;
end;

function TFrmBrwBase.GetErrorHTML(const ACode: integer; const AText: string): string;
begin
    Result := Format('<html><body><p>Error %d: %s</p></body></html>', [ACode, AText]);
end;

procedure TFrmBrwBase.Log(const AMsg: string);
begin
    PostTextMessage(Self.Handle, ISM_TMP_TEXT, ISM_TMP_BRW_LOG, AMsg);
end;

procedure TFrmBrwBase.Log(const AMsg, AValue: string);
begin
    Log(Format('%s: %s', [AMsg, AValue]));
end;

procedure TFrmBrwBase.Log(const AMsg: string; const AValue: boolean);
begin
    Log(AMsg, BoolToStr(AValue, True));
end;

function TFrmBrwBase.LogToClipboard: boolean;
var
    i: integer;
    s: TStringList;
begin
    i := lbxLog.ItemIndex;
    Result := i > -1;
    if Result then
    begin
        Clipboard.AsText := lbxLog.Items[i];
    end;
end;

procedure TFrmBrwBase.Init;

    procedure InitButton(ABtn: TSpeedButton);
    begin
        ABtn.ShowHint := True;
        ABtn.Flat := True;
        ABtn.Cursor := crHandPoint;
    end;

begin
    // Environment
    //------------------------------------------------------
    //FCore := ISCore;
    FCore := Application.MainForm as IISCore;

    // Options
    //------------------------------------------------------
    BlockContent := True;

    // Flags
    //------------------------------------------------------
    LastErrorCode := 0;
    LastErrorText := '';

    // Controls
    //------------------------------------------------------
    AddressHintHide;

    pnlTools.Width :=
        btnWebShowPage.Width + btnWebShowResource.Width + btnWebShowSource.Width +
        btnWebBlockContent.Width + btnWebFindInPage.Width;




    pnlTools.BorderSpacing.Left := 4;

    pnlStatusBar.BevelInner := bvNone;
    pnlStatusBar.BevelOuter := bvNone;
    pnlStatusBar.Height := 18;

    pnlStatus.Font.Size := 7;
    pnlStatus.Align := alRight;
    pnlStatus.BevelInner := bvNone;
    pnlStatus.BevelOuter := bvNone;
    pnlStatus.Color := clMoneyGreen;

    lblStatus.Font.Size := 7;

    lblHint.Font.Size := 7;

    SetBrwStatus('');
    SetBrwHint('');

    pnlHint.Width := 256;
    pnlHint.BevelInner := bvNone;
    pnlHint.BevelOuter := bvNone;
    pnlHint.Font.Size := 7;
    pnlHint.Align := alClient;
    pnlHint.Color := clSkyBlue;

    pnlAddressEdit.Color := clBtnShadow;
    pnlAddressEdit.BorderSpacing.Around := 2;

    edtAddress.BorderStyle := bsNone;
    edtAddress.BorderSpacing.Around := 1;

    pnlSearch.Visible := False;
    pnlSearchEdit.Color := clBtnShadow;
    pnlSearchEdit.BorderSpacing.Around := 2;
    edtSearch.BorderStyle := bsNone;
    edtSearch.BorderSpacing.Around := 1;
    lblSearchTitle.Caption := 'Find in page:';
    lblSearchTitle.BorderSpacing.Around := 2;

    //pnlAddressBar.BorderSpacing.Bottom := 1;
    //pnlAddressBar.Height := pnlAddressBar.Height + 1;

    // Buttons & actions
    //------------------------------------------------------
    actNavBack.Enabled := False;
    actNavForward.Enabled := False;

    InitButton(btnNavBack);
    InitButton(btnNavForward);
    InitButton(btnNavRefresh);
    InitButton(btnNavGo);

    InitButton(btnWebShowPage);
    InitButton(btnWebShowResource);
    InitButton(btnWebShowSource);
    InitButton(btnWebBlockContent);
    InitButton(btnWebFindInPage);

    // Progress
    //------------------------------------------------------
    tmrProgress.Interval := 100;
    imgProgress.Width := 16;

    // Images
    //------------------------------------------------------
    InitImages;
end;

procedure TFrmBrwBase.InitImages;
begin
    actBrw.Images := ISCore.Pic16;

    ISCore.SetActGlyph('resultset_previous', actNavBack);
    ISCore.SetActGlyph('resultset_next', actNavForward);
    ISCore.SetActGlyph('arrow_refresh', actNavRefresh);
    ISCore.SetActGlyph('cancel', actNavStop);
    ISCore.SetActGlyph('world_go', actNavGo);
    ISCore.SetActGlyph('page_green', actWebShowPage);
    ISCore.SetActGlyph('resource_usage', actWebShowResource);
    ISCore.SetActGlyph('html', actWebShowSource);
    ISCore.SetActGlyph('page_find', actFindInPage);
    ISCore.SetActGlyph('cb-off', actCBTurnOn);
    ISCore.SetActGlyph('cb-on', actCBTurnOff);

    // And buttons
    ISCore.SetBtnGlyph('resultset_previous', btnNavBack);
    ISCore.SetBtnGlyph('resultset_next', btnNavForward);
    ISCore.SetBtnGlyph('arrow_refresh', btnNavRefresh);
    ISCore.SetBtnGlyph('world_go', btnNavGo);
    ISCore.SetBtnGlyph('page_green', btnWebShowPage);
    ISCore.SetBtnGlyph('resource_usage', btnWebShowResource);
    ISCore.SetBtnGlyph('html', btnWebShowSource);
    ISCore.SetBtnGlyph('page_find', btnWebFindInPage);
    ISCore.SetBtnGlyph('cb-on', btnWebBlockContent);
end;

procedure TFrmBrwBase.DoTitleChange(const ATitle: string);
begin
    FTitle := ATitle;
    if Assigned(FOnTitleChange) then
        FOnTitleChange(Self, ATitle);
end;

procedure TFrmBrwBase.AddressHintHide;
begin
    pnlAddressHint.Visible := False;
    pnlAddressHint.SendToBack;
end;

procedure TFrmBrwBase.AddressHintShow;
begin
    with pnlAddressHint do
    begin
        Top := 0;
        Left := pnlAddress.Left + pnlAddressEdit.Left;
        Width := pnlAddressEdit.Width;
        Visible := True;
        BringToFront;
    end;
end;

procedure TFrmBrwBase.PostTextMessage(AHandle: HWND; AMsg: cardinal; ASubMsg: WParam; AText: string);
var
    s: PChar;
begin
    s := StrAlloc(Length(AText) + 1);
    StrPCopy(s, AText);
    PostMessage(AHandle, ISM_TMP_TEXT, ASubMsg, LParam(s));

    //Application.QueueAsyncCall();
end;

function TFrmBrwBase.GetTextMessage(const AMsg: TMessage): string;
begin
    Result := StrPas(PChar(AMsg.lParam));
end;

procedure TFrmBrwBase.LMTextMessage(var AMsg: TMessage);
var
    txt: string;
begin
    txt := GetTextMessage(AMsg);
    StrDispose(PChar(AMsg.lParam));
    case AMsg.wParam of
        ISM_TMP_BRW_RESOURCE:
        begin
            SetBrwStatus(txt);
        end;
        ISM_TMP_BRW_HINT:
        begin
            SetBrwHint(txt);
        end;
        ISM_TMP_BRW_LOG:
        begin
            DoLog(txt);
        end;
        ISM_TMP_BRW_POPUP:
        begin
            FCore.NewWebPage(txt, reCEF, True);
        end;
        ISM_TMP_BRW_POPUP_BACKGROUND:
        begin
            FCore.NewWebPage(txt, reCEF, False);
        end;
        ISM_TMP_BRW_LOAD_URL:
        begin
            LoadURL(txt);
        end;
        ISM_TMP_BRW_ADDRESS:
        begin
            SetBrwURL(txt);
        end;
    end;
end;

procedure TFrmBrwBase.SetBrwStatus(const AMsg: string);
begin
    lblStatus.Caption := AMsg;
    AdjustStatusPanel;
end;

procedure TFrmBrwBase.SetBrwHint(const AText: string);
begin
    lblHint.Caption := AText;
end;

procedure TFrmBrwBase.SetBrwURL(const aURL: string);
begin
    edtAddress.Text := aURL;

    // Write history entry
    ISCore.ModuleByCode('history').ExecuteCommand('add', [@aURL, @aURL]);
end;

procedure TFrmBrwBase.DoLog(const AMsg: string);
const
    { TODO -oND -cPerformance;Browser : Stop list updating while it invisible to improve performance }
    MaxLinesCount = 200;
var
    i: integer;
begin
    lbxLog.Items.BeginUpdate;
    try
        if lbxLog.Items.Count > MaxLinesCount then
        begin
            for i := lbxLog.Items.Count downto MaxLinesCount do
                lbxLog.Items.Delete(0);
        end;
        lbxLog.Items.Add(Format('%s: %s', [FormatDateTime('yyyy-mm-dd-hh-nn-ss', Now), AMsg]));
    finally
        lbxLog.Items.EndUpdate;
    end;
end;

procedure TFrmBrwBase.SetTitle(const AValue: string);
begin
    FTitle := AValue;
end;

function TFrmBrwBase.GetContainer: IISFrameContainer;
begin
    Result := FContainer;
end;

procedure TFrmBrwBase.SetContainer(AValue: IISFrameContainer);
begin
    if AValue = FContainer then
        Exit;
    FContainer := AValue;
end;

procedure TFrmBrwBase.DoSetStatus(const AMsg: string);
begin
    PostTextMessage(Self.Handle, ISM_TMP_TEXT, ISM_TMP_BRW_RESOURCE, AMsg);
end;

procedure TFrmBrwBase.DoSetHint(const AText: string);
begin
    PostTextMessage(Self.Handle, ISM_TMP_TEXT, ISM_TMP_BRW_HINT, AText);
end;

procedure TFrmBrwBase.LMProgressChange(var AMsg: TMessage);
begin
    InProgress := AMsg.wParam = 1;
end;

procedure TFrmBrwBase.ProgressStart;
begin
    PostMessage(Self.Handle, ISM_TMP_BRW_STATUS, 1, 0);
end;

procedure TFrmBrwBase.ProgressStop;
begin
    PostMessage(Self.Handle, ISM_TMP_BRW_STATUS, 0, 0);
end;

procedure TFrmBrwBase.AdjustStatusPanel;
begin
    if lblStatus.Caption = '' then
    begin
        pnlStatus.Visible := False;
    end
    else
    begin
        pnlStatus.Visible := True;
        pnlStatus.Width := (lblStatus.Left * 2) + lblStatus.Width;
    end;
end;

function TFrmBrwBase.ProcessMessage(AMsg: IISMessage): boolean;
begin
    Result := False;

    case AMsg.MsgType of
        msgBrowser:
        begin
            case AMsg.Msg of
                ISM_TMP_BRW_STATUS:
                begin

                end;
            end;
        end;
    end;
end;

function TFrmBrwBase.GetMTID: string;
begin
    Result := FMTID;
end;

constructor TFrmBrwBase.Create(TheOwner: TComponent);
begin
    inherited Create(TheOwner);
    Init;
end;

destructor TFrmBrwBase.Destroy;
begin
    Done;
    inherited Destroy;
end;

procedure TFrmBrwBase.LoadURL(const AUrl: string);
begin
    if edtAddress.Text <> AUrl then
        edtAddress.Text := AUrl;
end;

procedure TFrmBrwBase.DoGo;
var
    url: string;
begin
    url := GetSearchURL(edtAddress.Text);
    LoadURL(url);
end;

procedure TFrmBrwBase.FocusAddressField;
begin
    if edtAddress.CanFocus then
    begin
        edtAddress.SetFocus;
        edtAddress.SelectAll;
    end;
end;

end.
